# -*- coding: utf-8 -*-
# @Time     : 2020/7/7 2:28 下午
# @Author   : huangxiong
# @FileName : risk_decomposition_api.py
# @Comment  : 风险分解，api接口
# @Software : PyCharm

import pandas as pd
from quant_researcher.quant.datasource_fetch.portfolio_api.portfolio_tool import get_portfolio_fund_weight
from quant_researcher.quant.datasource_fetch.factor_api.factor_constant import \
    BARRA_FACTOR_NAME_DICT, BOND_FACTOR_NAME_DICT, BARRA_STYLE_FACTOR, BARRA_INDUSTRY_FACTOR
from quant_researcher.quant.risk_management.core_functions import risk_decomposition
from quant_researcher.quant.project_tool.db_operator import db_conn
from quant_researcher.quant.datasource_fetch.common_data_api.common_tools import get_max_date_of_table
from quant_researcher.quant.project_tool.exception import FOFServerError, ERR_S_2

FACTOR_NAME_DICT = BARRA_FACTOR_NAME_DICT.copy()
FACTOR_NAME_DICT.update(BOND_FACTOR_NAME_DICT)


def portfolio_risk_decomposition(calc_date, portfolio_id, customer_create, index_code, customer_index, calc_model):
    """

    :param calc_date: 计算日期，格式形如'2020-07-09'
    :param portfolio_id: 组合id
    :param customer_create: 是否是用户自建指数，1-是，0-否，默认0
    :param index_code: 基准指数代码
    :param customer_index: 用户自建指数，customer_create=1时必须传入该参数。
                           需要传入构建自建指数所需要的指数代码及权重，格式{"CBA00301.CS": 0.5, "000300": 0.5}
    :param calc_model: 计算模式，可选股票因子('s')、债券因子('b')、股债混合因子('s&b')
    :return: 组合和基准的每个因子的风险暴露、边际风险贡献、总风险贡献
    """
    # 根据组合代码找到持有的基金及权重
    calc_date_t = calc_date.replace('-', '')
    tmp_df = get_portfolio_fund_weight(portfolio_id, calc_date_t)
    if tmp_df is None:
        raise FOFServerError(ERR_S_2, f"该组合 {portfolio_id} 的id有问题")
    fund_list = tmp_df['fund_code'].tolist()
    weight_list = tmp_df['fund_weight'].tolist()

    res_df = risk_decomposition.risk_decomposition(calc_date, fund_list, weight_list,
                                                   customer_create, index_code, customer_index, calc_model)
    if res_df is None:
        raise FOFServerError(ERR_S_2, f"该组合的风险分解有问题，请检查")

    # factor_type用来区分风格因子、行业因子与其他因子
    res_df['factor_type'] = 0
    res_df['factor_type'].loc[res_df['factor_code'].isin(BARRA_STYLE_FACTOR)] = 1
    res_df['factor_type'].loc[res_df['factor_code'].isin(BARRA_INDUSTRY_FACTOR)] = 2

    res_list = res_df.to_dict(orient='records')

    return res_list


def portfolio_risk_chg(calc_date, compare_date, risk_type, portfolio_id, index_code, calc_model):
    """

    :param calc_date: str，计算日期，格式形如'2020-07-09'
    :param compare_date: str，比较日期，格式形如'2020-06-09'
    :param risk_type: str，风险类型，no_active代表组合本身的风险，active代表组合相对于基准的主动风险
    :param portfolio_id: str，组合id
    :param index_code: str，基准指数代码
    :param calc_model: str，计算模式，可选股票因子('stock')、债券因子('bond')、股债混合因子('stock_and_bond')
    :return: 组合每个因子的风险暴露、边际风险贡献、总风险贡献变动
    """
    df1 = portfolio_risk_decomposition(compare_date, portfolio_id, 0, index_code, None, calc_model)
    df1 = pd.DataFrame(df1).set_index('factor_code')
    df1.columns = [x + '_1' for x in df1.columns]
    df2 = portfolio_risk_decomposition(calc_date, portfolio_id, 0, index_code, None, calc_model)
    df2 = pd.DataFrame(df2).set_index('factor_code')
    df2.columns = [x + '_2' for x in df2.columns]

    df = df1.merge(df2, how='inner', on='factor_code')
    if risk_type == 'no_active':
        df = df[[x for x in df.columns if x[:9] == 'portfolio']]
        df['portfolio_exposure_chg'] = df['portfolio_exposure_2'] - df['portfolio_exposure_1']
        df['portfolio_mrc_chg'] = df['portfolio_mrc_2'] - df['portfolio_mrc_1']
        df['portfolio_trc_chg'] = df['portfolio_trc_2'] - df['portfolio_trc_1']
    else:
        df = df[[x for x in df.columns if x[:6] == 'active']]
        df['active_exposure_chg'] = df['active_exposure_2'] - df['active_exposure_1']
        df['active_mrc_chg'] = df['active_mrc_2'] - df['active_mrc_1']
        df['active_trc_chg'] = df['active_trc_2'] - df['active_trc_1']
    df = df.round(4)
    df = df.reset_index()

    factor_name_df = pd.DataFrame(list(FACTOR_NAME_DICT.items()), columns=['factor_code', 'factor_name'])
    df = df.merge(factor_name_df, how='left', on='factor_code')

    # factor_type用来区分风格因子、行业因子与其他因子
    df['factor_type'] = 0
    df['factor_type'].loc[df['factor_code'].isin(BARRA_STYLE_FACTOR)] = 1
    df['factor_type'].loc[df['factor_code'].isin(BARRA_INDUSTRY_FACTOR)] = 2
    res_list = df.to_dict(orient='records')

    return res_list


def portfolio_risk_decomposition_latest(portfolio_id, customer_create, index_code, customer_index):
    conn = db_conn.get_tk_factors_conn()
    end_date = get_max_date_of_table('tradedate', 'stk_barra_exposure', conn=conn)
    res = portfolio_risk_decomposition(end_date, portfolio_id, customer_create, index_code, customer_index, 's')

    return res


if __name__ == "__main__":
    # portfolio_risk_decomposition(calc_date='2020-08-04', portfolio_id='1076', index_code='000300', calc_model='s&b')
    # portfolio_risk_chg(calc_date='2020-07-09', compare_date='2020-06-09', risk_type='active',
    #                    portfolio_id='1076', index_code='000300', calc_model='s')
    portfolio_risk_decomposition_latest('1076', 0, '000300', None)
